#ifndef __UTILITY_H
#define __UTILITY_H

class SRGBA
{
public :
	SRGBA () : r(0), g(0), b(0), a(255) {}
	SRGBA (unsigned short _r, unsigned short _g, unsigned short _b, unsigned short _a)
		: r (_r), g(_g), b(_b), a(_a)
	{
	}
public :
	unsigned short r, g, b, a ;

};
class FRGBA 
{
public:
	FRGBA () : r(0), g(0), b(0), a(255) {}
	FRGBA (float _r, float _g, float _b, float _a)
		: r (_r), g(_g), b(_b), a(_a)
	{
	}
public :
	float r, g, b, a ;
};
FRGBA gamma_correction (SRGBA & srgba)
{

	return FRGBA(); 
}
SRGBA gamma_correction (FRGBA & srgba)
{
	return SRGBA(); 
}
 
class TexCoordf 
{
public:
	TexCoordf () : w(0.0f),h (0.0f) {}
	TexCoordf (float _w, float _h) : w(_w), h (_h) {}
public:
	float w, h ;
};


//In the texture memory, the first dimension is height. 
//Memory [Width][Height]
// Width -->> row: Height -->> col ;

class TextureMemory
{

public :
	TextureMemory(unsigned w, unsigned h) : m_Height( h), m_Width(w)
	{
		this->m_VideoMemory  = new float[4 * m_Height * m_Width] ; 
		this->m_HeightOffset = 1.0f / float (m_Height);
		this->m_WidthOffset = 1.0f / float (m_Width) ;
	}
	float getWidthOffset () const {return this->m_WidthOffset ;}
	float getHeightOffset () const {return this->m_HeightOffset ;}
	TexCoordf getTexCoord (unsigned w, unsigned h)
	{
		//return TexCoordf (w * m_WidthOffset, h * m_HeightOffset);
		return TexCoordf (w / float( m_Width ), h / float (m_Height));
		//return TexCoordf (w * m_WidthOffset, h * m_WidthOffset);
	}
	void write (unsigned x, unsigned y, const FRGBA & color )
	{
		unsigned offset = 4 * (y * (m_Width) + x ) ;
		m_VideoMemory[offset + 0] = color.r ;
		m_VideoMemory[offset + 1] = color.g ;
		m_VideoMemory[offset + 2] = color.b ;
		m_VideoMemory[offset + 3] = color.a ;
	}
	void write (unsigned x, unsigned y, unsigned offset, const float value)
	{
		offset += 4 * (y * (m_Width) + x ) ;
		m_VideoMemory[offset] = value ;
	}
	GLvoid * getMemory ()
	{
		return this->m_VideoMemory ;
	}
	~ TextureMemory()
	{
		if (m_VideoMemory)
		{
			delete m_VideoMemory ;
			m_VideoMemory = NULL ;
		}
	}
private: 
	float * m_VideoMemory;
	unsigned m_Height , m_Width ;
	float m_HeightOffset, m_WidthOffset ;
};

GLfloat compute_k(Point p[3])
{
	float a = (p[1]-p[2]).length(); 
	float b = (p[2]-p[0]).length();
	float c = (p[0]-p[1]).length();
	float k = (b*b + c * c - a * a) / (2.0 * c) ; 
	return GLfloat (k); 
}
GLfloat compute_h(Point p[3])
{
	float a = (p[1]-p[2]).length(); 
	float b = (p[2]-p[0]).length();
	float c = (p[0]-p[1]).length();
	float cos_a = (b*b + c* c - a* a)/ (2.0 * b * c) ;
	float sin_a = sqrt (1.0 - cos_a * cos_a) ;
	return (b * sin_a) ;
}
GLfloat compute_c(Point p[3])
{
	return (p[0] - p[1]).length();
}


class Face 
{
public :
	Face (TextureMemory & tm, unsigned _id, Point  p[3]) : 
		id(_id), ptm( & tm )
	{
		point[0] = p[0] ; point[1] = p[1] ; point[2] = p[2] ;
		compute_normalized_tex_param(); 
		
	}
	void write_tex_param (unsigned row, unsigned col)
	{
		TexCoordf texcoord = ptm->getTexCoord(row, col);
	
		std::cout<<"Value k "<<k<<std::endl;
		std::cout<<"Value h "<<h<<std::endl;
		std::cout<<"Value c "<<c<<std::endl;
		std::cout<<"Tex   w "<<texcoord.w<<std::endl;
		std::cout<<"Tex   h "<<texcoord.h<<std::endl;
		
		ptm->write (row, 1, FRGBA (texcoord.w, texcoord.h, 0.0f, 0.0f));
		ptm->write (row, 2, FRGBA (texcoord.w + c, texcoord.h , 0.0f, 0.0f));
		ptm->write (row, 3, FRGBA (texcoord.w + k, texcoord.h + h, 0.0f, 0.0f));
		
		// write color index 1
		ptm->write (row, 4, FRGBA (1.0f, 0.0f, 0.0f, 1.0f));
		ptm->write (row, 5, FRGBA (0.0f, 1.0f, 0.0f, 1.0f));
		ptm->write (row, 6, FRGBA (0.0f, 0.0f, 1.0f, 1.0f));
	}
	void draw(unsigned row, unsigned col)
	{
		TexCoordf texcoord = ptm->getTexCoord(row, col);

		std::cout<<"Value k "<<k<<std::endl;
		std::cout<<"Value h "<<h<<std::endl;
		std::cout<<"Value c "<<c<<std::endl;
		
		glBegin(GL_TRIANGLES);
		//glTexCoord2f(0.0, 0.0); glVertex3f(2.0 - 4.0 /* * col / row */ , -2.0, 0.0);//
		//glTexCoord2f(0.0, 1.0); glVertex3f(2.0, -2.0, 0.0);
		//glTexCoord2f(1.0, 1.0); glVertex3f(2.0, 2.0, 0.0);
		
		//glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0);
		//glTexCoord2f(c, 0.0 ); glVertex3f(c, 0.0, 0.0);
		//glTexCoord2f(k, h); glVertex3f(k , h, 0.0);
		
		glTexCoord2f(texcoord.w, 		texcoord.h); glVertex3f(point[0].x, point[0].y, point[0].z);
		glTexCoord2f(texcoord.w + c , 	texcoord.h ); glVertex3f(point[1].x, point[1].y, point[1].z);
		glTexCoord2f(texcoord.w + k , 	texcoord.h + h); glVertex3f(point[2].x, point[2].y, point[2].z);
		
		glEnd();		
	}
private :
	void compute_normalized_tex_param ()
	{
		float w_off = ptm->getWidthOffset ();
		float h_off = ptm->getHeightOffset();

		float off = w_off < h_off ? w_off : h_off ;

		k = compute_k(point);
		h = compute_h(point);
		c = compute_c(point);


		if (h >=c)
		{
			k /= h ; c/=h ; h = 1.0f ; 
		}
		else
		{
			k /= c; h /= c; c = 1.0;
		}

		k *= off ; h*= off ; c *= off  ;
		//k *= w_off ; h*= h_off ; c *= w_off  ;
	}
private:

	unsigned id ;
	float k, h, c ;
	TextureMemory * ptm ;
	
public :
	Point point[3];
};

#endif